import math
import operator

import numpy as np

import matplotlib.pyplot as plt

# 以下代码从全局设置字体为Arial Unicode MS，解决显示中文问题【mac】
# 设置font.sans-serif 或 font.family 均可
from numpy import tile, array

plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
# plt.rcParams['font.size'] = 12
# plt.rcParams['font.family']=['Arial Unicode MS']
# 解决中文字体下坐标轴负数的负号显示问题
plt.rcParams['axes.unicode_minus'] = False

# rc方法，其实和设置rcParams 基本等效
# 设置font字典为 SimSun（宋体），大小为12（默认为10）
font = {'family': 'Arial Unicode MS',
        'size': '12'}
# 设置 字体
plt.rc('font', **font)
# 解决中文字体下坐标轴负数的负号显示问题
plt.rc('axes', unicode_minus=False)


# 创建数据源，返回数据集和类标签
def create_dataset():
    datasets = np.array([[8, 4, 2], [7, 1, 1], [1, 4, 4], [3, 0, 5]])  # 数据集
    labels = ['非常热', '非常热', '一般热', '一般热']  # 类标签
    return datasets, labels


def create_dataset_skl():
    datasets = np.array([[8, 4, 2], [7, 1, 1], [1, 4, 4], [3, 0, 5], [9, 4, 2], [7, 0, 1], [1, 5, 4], [4, 0, 5]])  # 数据集
    labels = [0, 0, 1, 1, 0, 0, 1, 1]  # 类标签
    return datasets, labels


# 可视化分析数据
def analyze_data_plot(x, y):
    fig = plt.figure()
    # 将画布划分1行1列1块
    ax = fig.add_subplot(111)
    ax.scatter(x, y)
    # plt.scatter(x, y)

    # 设置散点图标题和横纵坐标
    plt.title('游客冷热感知点', fontsize=12)
    plt.xlabel('天热吃冰激凌数目', fontdict={'fontsize': 12})

    # 保存截图
    plt.savefig('datasets_plot.png', format='png', bbox_inches='tight')
    plt.show()


# 构造KNN分类器
def knn_Classifier(newV, datasets, labels, k):
    # 1 获取新的样本数据
    # 2 获取样本库的数据
    # 3 选择k值
    # 4 计算样本数据与样本库数据之间的距离
    sqrtDist = computerEuclideanDistance3(newV, datasets)
    # 5 根据距离进行排序
    sortedDistIndexs = sqrtDist.argsort(axis=0)
    print(sortedDistIndexs)
    # 6 针对k个点，统计各个类别的数量
    classCount = {}  # 统计各个类别分别的数量
    for i in range(k):
        # 根据距离排序索引值找到类标签
        votelabel = labels[sortedDistIndexs[i]]
        print(sortedDistIndexs[i], votelabel)
        # 统计标签健值对
        classCount[votelabel] = classCount.get(votelabel, 0) + 1
    print(classCount)
    # 7 投票机制，少数服从多数原则，输入类别
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=1)
    print(newV, 'KNN投票预测结果是：', sortedClassCount[0][0])
    return sortedClassCount[0][0]


# 欧式距离计算：d2=(x1-x2)2+(y1-y2)2
def computerEuclideanDistance1(x1, y1, x2, y2):
    d = math.sqrt(math.pow((x1 - x2), 2) + math.pow((y1 - y2), 2))
    return d


# 欧式距离计算2
def computerEuclideanDistance2(instance1, instance2, length):
    d = 0
    for x in range(length):
        d += math.pow((instance1[x] - instance2[x]), 2)
    return math.sqrt(d)


# 欧式距离计算3
def computerEuclideanDistance3(newV, datasets):
    rowsize, colsize = datasets.shape
    print(tile(newV, (rowsize, 1)))
    # 各特征向量间作差值
    diffMat = tile(newV, (rowsize, 1)) - datasets
    print(diffMat)
    # 对差值平方
    sqDiffMat = diffMat ** 2
    print(sqDiffMat)
    # 差值平方和进行开方
    sqrtDist = sqDiffMat.sum(axis=1) ** 0.5
    print(sqrtDist)
    return sqrtDist


# 利用KNN分类器预测随机访客天气感知度
def predictTemperature():
    # 1 创建数据集和类标签
    datasets, labels = create_dataset()
    # 2 采访新游客
    iceCream = float(input('Q:请问你今天吃了几个冰激凌？\n'))
    drinkWater = float(input('Q:请问你今天喝了几瓶（杯）水？\n'))
    playAct = float(input('Q:请问你今天户外活动几个小时？\n'))
    newV = array([iceCream, drinkWater, playAct])
    res = knn_Classifier(newV, datasets, labels, 3)
    return res


if __name__ == '__main__':
    # 1.创建数据集和类标签
    datasets, labels = create_dataset()
    print('数据集:\n', datasets, '\n类标签:\n', labels)

    # 可视化分析数据
    analyze_data_plot(datasets[:, 0], datasets[:, 1])

    # 欧式距离计算1
    d = computerEuclideanDistance1(2, 4, 8, 2)
    print('欧式距离计算1:', d)

    # 欧式距离计算2
    d = computerEuclideanDistance2([2, 4], [8, 2], 2)
    print('欧式距离计算2:', d)

    # 欧式距离计算3
    d = computerEuclideanDistance3([2, 4, 4], datasets)
    print('欧式距离计算3:', d)

    # KNN分类器
    newV = [2, 4, 4]
    res = knn_Classifier(newV, datasets, labels, 3)
    print(newV, 'KNN投票预测结果是：', res)

    vecs = array([[2, 4, 4], [3, 0, 0], [5, 7, 2]])
    for vec in vecs:
        res = knn_Classifier(vec, datasets, labels, 3)
        print(vec, 'KNN投票预测结果是：', res)

    res = predictTemperature()
    print('该访客任务成都天气是：', res)
